%%--------------------------------------------------------------------
%% Author: <vjache>
%% Created: 03.04.2009
%% Description: Mnesia powered vector module.
%%--------------------------------------------------------------------

-module(vect_mnesia).


%%--------------------------------------------------------------------
%% Include files
%%--------------------------------------------------------------------

-include("../include/vect.hrl").
-include("../include/util.hrl").

%%--------------------------------------------------------------------
%% Exported Functions
%%--------------------------------------------------------------------
-export([
		 new/2,
		 new_mem_bag/2,
		 close/1,
		 delete/2,
		 insert/2,
		 lookup/2,
		 delete_key/2,
		 all/1,
		 clear/1,
		 activity/1,
		 activity/2,
		 test/1
		]).

%%--------------------------------------------------------------------
%% API Functions
%%--------------------------------------------------------------------

new(VectName,{disc_only,RecordName,Attrs,FragNum}) ->
	#vect
	{
	 drv_module=?MODULE,
	 open_fun=fun()->
					  ConfigBase=[{attributes, Attrs},
								  {record_name, RecordName},
								  {disc_only_copies, [node()]},
								  {index, lists:seq(3,length(Attrs)+1)},
								  {type, bag}],
					  Config=case FragNum of
								 undefined ->
									 ConfigBase;
								 _ ->
									 FragProps=
										 {frag_properties,[{n_fragments, FragNum},
														   {n_disc_only_copies, 1},
														   {node_pool,[node()]}]},
									 [FragProps|ConfigBase]
							 end,
					  RecName=get_record_name(VectName,Config),
					  TableName=get_table_name(VectName, Config),
					  case mnesia:create_table(TableName, Config) of
						  {atomic,ok} -> {TableName, RecName};
						  {aborted,{already_exists,_TableName}}=Dbg ->
							  ?LOG_TRACE("DEBUG ~p~n",[Dbg]),
							  %erlang:process_display(self(), backtrace),
							  if 
								  TableName==VectName ->
									  ok=do_local_copy(TableName),
									  {TableName, RecName};
								  true ->
									  {TableName, RecName}
							  end;
						  {aborted,Reason} ->
							  exit({create_vect_abort,node(),Reason})
					  end					  
			  end,
	 close_fun=fun close/1,
	 delete_fun=fun delete/2,
	 delete_key_fun=fun delete_key/2,
	 insert_fun=fun insert/2,
	 lookup_fun=fun lookup/2,
	 all_fun=fun all/1,
	 clear_fun=fun clear/1
	};
new(VectName,Config) ->
	#vect
	{
	 drv_module=?MODULE,
	 open_fun=fun()->
					  RecName=get_record_name(VectName,Config),
					  TableName=get_table_name(VectName, Config),
					  case mnesia:create_table(TableName, Config) of
						  {atomic,ok} -> {TableName, RecName};
						  {aborted,{already_exists,TableName}} ->
							  if 
								  TableName==VectName ->
									  ok=do_local_copy(TableName),
									  {TableName, RecName};
								  true ->
									  {TableName, RecName}
							  end;
						  {aborted,Reason} ->
							  exit({create_vect_abort,node(),Reason})
					  end					  
			  end,
	 close_fun=fun close/1,
	 delete_fun=fun delete/2,
	 delete_key_fun=fun delete_key/2,
	 insert_fun=fun insert/2,
	 lookup_fun=fun lookup/2,
	 all_fun=fun all/1,
	 clear_fun=fun clear/1
	}.
get_record_name(VectName,Config) ->
	case lists:keysearch(record_name, 1, Config) of
		false->
			VectName;
		{value,{record_name,RN}} ->
			RN
	end.
get_table_name(VectName, Config) ->
	case lists:keysearch(local_content, 1, Config) of
		false->
			list_to_atom(
			  lists:flatten(
				io_lib:format("~s.~s", 
							  [atom_to_list(VectName),
							   atom_to_list(node())])));
		{value,{local_content,false}} ->
			get_table_name(VectName,lists:keydelete(local_content, 1, Config));
		{value,{local_content,true}} ->
			VectName
	end.

new_mem_bag(VectName,Attributes) ->
	new(VectName,[{attributes, Attributes},{type, bag},{local_content, true}]).

%% open(VectName) ->
%% 	VectName.

close(_VectName) ->
	ok.
delete({VectName,RecName}, Tuple) ->
	activity(fun()->
					 ok=mnesia:delete_object(VectName,to_record(RecName, Tuple),write)
			 end).
insert({VectName,RecName}, Tuples) when is_list(Tuples)->
	lists:foreach(fun(Tuple)->
						  ok=insert({VectName,RecName}, Tuple)
				  end, Tuples);
insert({VectName,RecName}, Tuple) when is_tuple(Tuple)-> 
	activity(fun()->
%% 					 io:format("DBG MNESIA VECT INSERT ~p~p~n",[Tuple,node()]),
					 ok=mnesia:write(VectName,to_record(RecName, Tuple),write)
			 end).
lookup({VectName,_RecName}, Key) ->
	activity(fun()->
					 iter:from_list(
					   %% 					   to_tuples(mnesia:read({VectName, Key}))
					   mnesia:read({VectName, Key})
										)
			 end).
delete_key({VectName,_RecName}, Key) ->
	activity(fun()->
					 ok=mnesia:delete({VectName, Key})
			 end).
all({VectName,_RecName}) -> 
	vect_mnesia_iter_all:new(VectName,1000). 
clear({VectName,_RecName}) ->
	case mnesia:clear_table(VectName) of
		{atomic, ok} ->
			ok;
		{aborted, R} ->
			exit({vect_cleanup_fail,R})
	end.


%%--------------------------------------------------------------------
%% Local Functions
%%--------------------------------------------------------------------

do_local_copy(VectName) ->
	case mnesia:table_info(VectName, ram_copies) of
		[] ->
			ok;
		RamNodes ->
			case lists:member(node(), RamNodes) of
				true ->
					ok;
				false ->
					{atomic,ok}=mnesia:add_table_copy(VectName, node(), ram_copies)
			end
	end,
	case mnesia:table_info(VectName, disc_copies) of
		[] ->
			ok;
		DiskNodes ->
			case lists:member(node(), DiskNodes) of
				true ->
					ok;
				false ->
					{atomic,ok}=mnesia:add_table_copy(VectName, node(), disc_copies)
			end
	end,
	case mnesia:table_info(VectName, disc_only_copies) of
		[] ->
			ok;
		DiskOnlyNodes ->
			case lists:member(node(), DiskOnlyNodes) of
				true ->
					ok;
				false ->
					{atomic,ok}=mnesia:add_table_copy(VectName, node(), disc_only_copies)
			end
	end,
	ok.


to_record(RecName, Tuple) ->
	case element(1, Tuple) of
		RecName ->
			Tuple;
		_ ->
			list_to_tuple([RecName|tuple_to_list(Tuple)])
	end.

%% to_tuples(Records) ->
%% 	[
%% 	 begin 
%% 		 [_|Rest]=tuple_to_list(Record),
%% 		 list_to_tuple(Rest)
%% 	 end||Record<-Records].


activity(Fun) ->
	activity(Fun,[]).
activity(Fun, Args) ->
	mnesia:activity(async_dirty, Fun, Args,mnesia_frag).

test(File) ->
	V=vect_mnesia:new(test,{disc_only,spog,[s,p,o,g],100}),
	Pipe=pipe_vect:new(V),
	{Is,Ps}=pipe:push_each_prog(Pipe, iter_disk_log:new([{name,"Log"},{file,File}]),
						   fun(Count) ->
								   Mod=(Count rem 10000),
								   case Mod of
									0 ->
										io:format("[~p] Loaded ~p~n",[now(),Count]);
									_ ->
										ok
								   end
						   end),
	ok=iter:close(Is),
	ok=pipe:close(Ps).
